use serde::{Deserialize, Serialize};
use rust_decimal::Decimal;
use rust_decimal::prelude::*;

/// =======================
/// OrderRequest → Raw
/// =======================

#[derive(Debug, Clone)]
pub struct OrderRequest {
    pub symbol: String,
    pub side: String,
    pub position_side: Option<String>,
    pub order_type: String,
    pub time_in_force: Option<String>,
    pub quantity: Option<Decimal>,
    pub reduce_only: Option<bool>,
    pub price: Option<Decimal>,
    pub new_client_order_id: Option<String>,
    pub stop_price: Option<Decimal>,
    pub close_position: Option<bool>,
    pub activation_price: Option<Decimal>,
    pub callback_rate: Option<Decimal>,
    pub working_type: Option<String>,
    pub price_protect: Option<bool>,
    pub new_order_resp_type: Option<String>,
    pub price_match: Option<String>,
    pub self_trade_prevention_mode: Option<String>,
    pub good_till_date: Option<u64>,
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

impl OrderRequest {
    pub fn from_model(&self) -> OrderRequestRaw {
        OrderRequestRaw {
            symbol: self.symbol.clone(),
            side: self.side.clone(),
            position_side: self.position_side.clone(),
            order_type: self.order_type.clone(),
            time_in_force: self.time_in_force.clone(),
            quantity: self.quantity.as_ref().map(|x| x.to_string()),
            reduce_only: self.reduce_only.map(|x| x.to_string()),
            price: self.price.as_ref().map(|x| x.to_string()),
            new_client_order_id: self.new_client_order_id.clone(),
            stop_price: self.stop_price.as_ref().map(|x| x.to_string()),
            close_position: self.close_position.map(|x| x.to_string()),
            activation_price: self.activation_price.as_ref().map(|x| x.to_string()),
            callback_rate: self.callback_rate.as_ref().map(|x| x.to_string()),
            working_type: self.working_type.clone(),
            price_protect: self.price_protect.map(|x| x.to_string()),
            new_order_resp_type: self.new_order_resp_type.clone(),
            price_match: self.price_match.clone(),
            self_trade_prevention_mode: self.self_trade_prevention_mode.clone(),
            good_till_date: self.good_till_date,
            recv_window: self.recv_window,
            timestamp: self.timestamp,
            signature: self.signature.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct OrderRequestRaw {
    #[serde(rename = "symbol")]
    pub symbol: String,
    #[serde(rename = "side")]
    pub side: String,
    #[serde(rename = "positionSide")]
    pub position_side: Option<String>,
    #[serde(rename = "type")]
    pub order_type: String,
    #[serde(rename = "timeInForce")]
    pub time_in_force: Option<String>,
    #[serde(rename = "quantity")]
    pub quantity: Option<String>,
    #[serde(rename = "reduceOnly")]
    pub reduce_only: Option<String>,
    #[serde(rename = "price")]
    pub price: Option<String>,
    #[serde(rename = "newClientOrderId")]
    pub new_client_order_id: Option<String>,
    #[serde(rename = "stopPrice")]
    pub stop_price: Option<String>,
    #[serde(rename = "closePosition")]
    pub close_position: Option<String>,
    #[serde(rename = "activationPrice")]
    pub activation_price: Option<String>,
    #[serde(rename = "callbackRate")]
    pub callback_rate: Option<String>,
    #[serde(rename = "workingType")]
    pub working_type: Option<String>,
    #[serde(rename = "priceProtect")]
    pub price_protect: Option<String>,
    #[serde(rename = "newOrderRespType")]
    pub new_order_resp_type: Option<String>,
    #[serde(rename = "priceMatch")]
    pub price_match: Option<String>,
    #[serde(rename = "selfTradePreventionMode")]
    pub self_trade_prevention_mode: Option<String>,
    #[serde(rename = "goodTillDate")]
    pub good_till_date: Option<u64>,
    #[serde(rename = "recvWindow")]
    pub recv_window: Option<u64>,
    #[serde(rename = "timestamp")]
    pub timestamp: u64,
    #[serde(rename = "signature")]
    pub signature: String,
}

/// =======================
/// ModifyOrderRequest → Raw
/// =======================

#[derive(Debug, Clone)]
pub struct ModifyOrderRequest {
    pub symbol: String,
    pub order_id: Option<u64>,
    pub orig_client_order_id: Option<String>,
    pub side: String,
    pub quantity: Decimal,
    pub price: Decimal,
    pub price_match: Option<String>,
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

impl ModifyOrderRequest {
    pub fn from_model(&self) -> ModifyOrderRequestRaw {
        ModifyOrderRequestRaw {
            order_id: self.order_id,
            orig_client_order_id: self.orig_client_order_id.clone(),
            symbol: self.symbol.clone(),
            side: self.side.clone(),
            quantity: self.quantity.to_string(),
            price: self.price.to_string(),
            price_match: self.price_match.clone(),
            recv_window: self.recv_window,
            timestamp: self.timestamp,
            signature: self.signature.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct ModifyOrderRequestRaw {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub orig_client_order_id: Option<String>,
    pub symbol: String,
    pub side: String,
    pub quantity: String,
    pub price: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub price_match: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

/// =======================
/// CancelOrderRequest → Raw
/// =======================

#[derive(Debug, Clone)]
pub struct CancelOrderRequest {
    pub symbol: String,
    pub order_id: Option<u64>,
    pub orig_client_order_id: Option<String>,
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

impl CancelOrderRequest {
    pub fn from_model(&self) -> CancelOrderRequestRaw {
        CancelOrderRequestRaw {
            symbol: self.symbol.clone(),
            order_id: self.order_id,
            orig_client_order_id: self.orig_client_order_id.clone(),
            recv_window: self.recv_window,
            timestamp: self.timestamp,
            signature: self.signature.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct CancelOrderRequestRaw {
    pub symbol: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub orig_client_order_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

/// =======================
/// QueryOrderRequest → Raw
/// =======================

#[derive(Debug, Clone)]
pub struct QueryOrderRequest {
    pub symbol: String,
    pub order_id: Option<u64>,
    pub orig_client_order_id: Option<String>,
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

impl QueryOrderRequest {
    pub fn from_model(&self) -> QueryOrderRequestRaw {
        QueryOrderRequestRaw {
            symbol: self.symbol.clone(),
            order_id: self.order_id,
            orig_client_order_id: self.orig_client_order_id.clone(),
            recv_window: self.recv_window,
            timestamp: self.timestamp,
            signature: self.signature.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct QueryOrderRequestRaw {
    pub symbol: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub orig_client_order_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

/// =======================
/// QueryAllOrdersRequest → Raw
/// =======================

#[derive(Debug, Clone)]
pub struct QueryAllOrdersRequest {
    pub symbol: String,
    pub order_id: Option<u64>,
    pub start_time: Option<u64>,
    pub end_time: Option<u64>,
    pub limit: Option<u32>,
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

impl QueryAllOrdersRequest {
    pub fn from_model(&self) -> QueryAllOrdersRequestRaw {
        QueryAllOrdersRequestRaw {
            symbol: self.symbol.clone(),
            order_id: self.order_id,
            start_time: self.start_time,
            end_time: self.end_time,
            limit: self.limit,
            recv_window: self.recv_window,
            timestamp: self.timestamp,
            signature: self.signature.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct QueryAllOrdersRequestRaw {
    pub symbol: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub order_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub start_time: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub end_time: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub limit: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

/// =======================
/// QueryOpenOrdersRequest → Raw
/// =======================

#[derive(Debug, Clone)]
pub struct QueryOpenOrdersRequest {
    pub symbol: Option<String>,
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

impl QueryOpenOrdersRequest {
    pub fn from_model(&self) -> QueryOpenOrdersRequestRaw {
        QueryOpenOrdersRequestRaw {
            symbol: self.symbol.clone(),
            recv_window: self.recv_window,
            timestamp: self.timestamp,
            signature: self.signature.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct QueryOpenOrdersRequestRaw {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub symbol: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
    pub signature: String,
}

/// =======================
/// OrderResponseRaw → OrderResponse
/// =======================

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OrderResponseRaw {
    pub client_order_id: String,
    pub cum_qty: String,
    pub cum_quote: String,
    pub executed_qty: String,
    pub order_id: u64,
    pub avg_price: String,
    pub orig_qty: String,
    pub price: String,
    pub reduce_only: bool,
    pub side: String,
    pub position_side: String,
    pub status: String,
    pub stop_price: String,
    pub close_position: bool,
    pub symbol: String,
    pub time_in_force: String,
    #[serde(rename = "type")]
    pub order_type: String,
    pub orig_type: String,
    pub activate_price: Option<String>,
    pub price_rate: Option<String>,
    pub update_time: u64,
    pub working_type: String,
    pub price_protect: bool,
    pub price_match: Option<String>,
    pub self_trade_prevention_mode: Option<String>,
    pub good_till_date: Option<u64>,
}

impl OrderResponseRaw {
    pub fn to_model(&self) -> Option<OrderResponse> {
        Some(OrderResponse {
            client_order_id: self.client_order_id.clone(),
            cum_qty: self.cum_qty.parse().ok()?,
            cum_quote: self.cum_quote.parse().ok()?,
            executed_qty: self.executed_qty.parse().ok()?,
            order_id: self.order_id,
            avg_price: self.avg_price.parse().ok()?,
            orig_qty: self.orig_qty.parse().ok()?,
            price: self.price.parse().ok()?,
            reduce_only: self.reduce_only,
            side: self.side.clone(),
            position_side: self.position_side.clone(),
            status: self.status.clone(),
            stop_price: self.stop_price.parse().ok()?,
            close_position: self.close_position,
            symbol: self.symbol.clone(),
            time_in_force: self.time_in_force.clone(),
            order_type: self.order_type.clone(),
            orig_type: self.orig_type.clone(),
            activate_price: self.activate_price.as_ref().map(|x| x.parse().ok()).flatten(),
            price_rate: self.price_rate.as_ref().map(|x| x.parse().ok()).flatten(),
            update_time: self.update_time,
            working_type: self.working_type.clone(),
            price_protect: self.price_protect,
            price_match: self.price_match.clone(),
            self_trade_prevention_mode: self.self_trade_prevention_mode.clone(),
            good_till_date: self.good_till_date,
        })
    }
}

#[derive(Debug, Clone)]
pub struct OrderResponse {
    pub client_order_id: String,
    pub cum_qty: Decimal,
    pub cum_quote: Decimal,
    pub executed_qty: Decimal,
    pub order_id: u64,
    pub avg_price: Decimal,
    pub orig_qty: Decimal,
    pub price: Decimal,
    pub reduce_only: bool,
    pub side: String,
    pub position_side: String,
    pub status: String,
    pub stop_price: Decimal,
    pub close_position: bool,
    pub symbol: String,
    pub time_in_force: String,
    pub order_type: String,
    pub orig_type: String,
    pub activate_price: Option<Decimal>,
    pub price_rate: Option<Decimal>,
    pub update_time: u64,
    pub working_type: String,
    pub price_protect: bool,
    pub price_match: Option<String>,
    pub self_trade_prevention_mode: Option<String>,
    pub good_till_date: Option<u64>,
}

/// ========================
/// ChangeLeverageRequestRaw（用于发送请求）
/// ========================

#[derive(Debug, Serialize)]
pub struct ChangeLeverageRequestRaw {
    pub symbol: String,
    pub leverage: u32,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
}

/// ========================
/// ChangeLeverageResponseRaw → ChangeLeverageResponse
/// ========================

#[derive(Debug, Deserialize)]
pub struct ChangeLeverageResponseRaw {
    pub leverage: u32,
    pub maxNotionalValue: String,
    pub symbol: String,
}

impl ChangeLeverageResponseRaw {
    pub fn to_model(&self) -> Option<ChangeLeverageResponse> {
        Some(ChangeLeverageResponse {
            leverage: self.leverage,
            max_notional_value: self.maxNotionalValue.parse().ok()?,
            symbol: self.symbol.clone(),
        })
    }
}

/// ========================
/// ChangeLeverageResponse
/// ========================

#[derive(Debug, Clone)]
pub struct ChangeLeverageResponse {
    pub leverage: u32,
    pub max_notional_value: Decimal,
    pub symbol: String,
}
/// ========================
/// ChangeMarginTypeRequestRaw（发起请求）
/// ========================

#[derive(Debug, Serialize)]
pub struct ChangeMarginTypeRequestRaw {
    pub symbol: String,
    pub marginType: String, // "ISOLATED" 或 "CROSSED"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
}

/// ========================
/// ChangeMarginTypeResponseRaw → ChangeMarginTypeResponse
/// ========================

#[derive(Debug, Deserialize)]
pub struct ChangeMarginTypeResponseRaw {
    pub code: i32,
    pub msg: String,
}

impl ChangeMarginTypeResponseRaw {
    pub fn to_model(self) -> ChangeMarginTypeResponse {
        ChangeMarginTypeResponse {
            code: self.code,
            message: self.msg,
        }
    }
}

/// ========================
/// ChangeMarginTypeResponse
/// ========================

#[derive(Debug, Clone)]
pub struct ChangeMarginTypeResponse {
    pub code: i32,
    pub message: String,
}


/// ========================
/// PositionRiskRaw → PositionRisk
/// ========================

#[derive(Debug, Deserialize)]
pub struct PositionRiskRaw {
    pub symbol: String,
    pub positionSide: String,
    pub positionAmt: String,
    pub entryPrice: String,
    pub breakEvenPrice: String,
    pub markPrice: String,
    pub unRealizedProfit: String,
    pub liquidationPrice: String,
    pub isolatedMargin: String,
    pub notional: String,
    pub marginAsset: String,
    pub isolatedWallet: String,
    pub initialMargin: String,
    pub maintMargin: String,
    pub positionInitialMargin: String,
    pub openOrderInitialMargin: String,
    pub adl: u8,
    pub bidNotional: String,
    pub askNotional: String,
    pub updateTime: u64,
}

impl PositionRiskRaw {
    pub fn to_model(self) -> Option<PositionRisk> {
        Some(PositionRisk {
            symbol: self.symbol,
            position_side: self.positionSide,
            position_amt: self.positionAmt.parse().ok()?,
            entry_price: self.entryPrice.parse().ok()?,
            break_even_price: self.breakEvenPrice.parse().ok()?,
            mark_price: self.markPrice.parse().ok()?,
            unrealized_profit: self.unRealizedProfit.parse().ok()?,
            liquidation_price: self.liquidationPrice.parse().ok()?,
            isolated_margin: self.isolatedMargin.parse().ok()?,
            notional: self.notional.parse().ok()?,
            margin_asset: self.marginAsset,
            isolated_wallet: self.isolatedWallet.parse().ok()?,
            initial_margin: self.initialMargin.parse().ok()?,
            maint_margin: self.maintMargin.parse().ok()?,
            position_initial_margin: self.positionInitialMargin.parse().ok()?,
            open_order_initial_margin: self.openOrderInitialMargin.parse().ok()?,
            adl: self.adl,
            bid_notional: self.bidNotional.parse().ok()?,
            ask_notional: self.askNotional.parse().ok()?,
            update_time: self.updateTime,
        })
    }
}

#[derive(Debug, Clone)]
pub struct PositionRisk {
    pub symbol: String,
    pub position_side: String,
    pub position_amt: Decimal,
    pub entry_price: Decimal,
    pub break_even_price: Decimal,
    pub mark_price: Decimal,
    pub unrealized_profit: Decimal,
    pub liquidation_price: Decimal,
    pub isolated_margin: Decimal,
    pub notional: Decimal,
    pub margin_asset: String,
    pub isolated_wallet: Decimal,
    pub initial_margin: Decimal,
    pub maint_margin: Decimal,
    pub position_initial_margin: Decimal,
    pub open_order_initial_margin: Decimal,
    pub adl: u8,
    pub bid_notional: Decimal,
    pub ask_notional: Decimal,
    pub update_time: u64,
}

/// ========================
/// ChangePositionModeRequestRaw（请求结构体）
/// ========================

#[derive(Debug, Serialize)]
pub struct ChangePositionModeRequestRaw {
    pub dualSidePosition: String, // "true" 或 "false"
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recv_window: Option<u64>,
    pub timestamp: u64,
}

/// ========================
/// ChangePositionModeResponseRaw → ChangePositionModeResponse
/// ========================

#[derive(Debug, Deserialize)]
pub struct ChangePositionModeResponseRaw {
    pub code: i32,
    pub msg: String,
}

impl ChangePositionModeResponseRaw {
    pub fn to_model(self) -> ChangePositionModeResponse {
        ChangePositionModeResponse {
            code: self.code,
            message: self.msg,
        }
    }
}

/// ========================
/// ChangePositionModeResponse
/// ========================

#[derive(Debug, Clone)]
pub struct ChangePositionModeResponse {
    pub code: i32,
    pub message: String,
}